#include <iostream>

#define ASIO_STANDALONE
#include <asio.hpp>
#include <asio/ts/buffer.hpp>
#include <asio/ts/internet.hpp>

std::vector<char> vBuffer(1 * 1024);
void GrabSomeData(asio::ip::tcp::socket &socket);

int main()
{
    asio::error_code ec;

    asio::io_context context;

    // get some fake tasks to asio
    // so that the context won't exit immediately
    asio::io_context::work idleWork(context);

    // Allow the context to run its own thread
    // so that it won't block the main programe
    std::thread thrContext = std::thread([&]() { context.run(); });

    // www.baidu.com
    // asio::ip::tcp::endpoint endpoint(asio::ip::make_address("110.242.68.66", ec), 80);
    // example.com : normal context
    // asio::ip::tcp::endpoint endpoint(asio::ip::make_address("93.184.216.34", ec), 80);
    // community.onelonecoder.com : long context
    asio::ip::tcp::endpoint endpoint(asio::ip::make_address("51.38.81.49", ec), 80);

    asio::ip::tcp::socket socket(context);

    socket.connect(endpoint, ec);

    if (!ec)
    {
        std::cout << "Connected!" << std::endl;
    }
    else
    {
        std::cout << "Failed to connect to address:\n"
                  << ec.message() << std::endl;
    }

#if 0
	if (socket.is_open())
	{
		std::string sRequest =
			"GET /index.html HTTP/1.1\r\n"
			"Host: example.com\r\n"
			"Connection: close\r\n\r\n";

		socket.write_some(asio::buffer(sRequest.data(), sRequest.size()), ec);

#if 0
		using namespace std::chrono_literals;
		std::this_thread::sleep_for(200ms);
#else
        socket.wait(socket.wait_read);
#endif

        size_t bytes = socket.available();
		std::cout << "Bytes Available: " << bytes << std::endl;

		if (bytes > 0)
		{
			std::vector<char> vBuffer(bytes);
			socket.read_some(asio::buffer(vBuffer.data(), vBuffer.size()), ec);

			for (auto c : vBuffer) {
				std::cout << c;
			}
			std::cout << std::endl;
		}
	}
#else
	if (socket.is_open())
	{
		// Think asynchronously
		GrabSomeData(socket);

		std::string sRequest =
			"GET /index.html HTTP/1.1\r\n"
			"Host: example.com\r\n"
			"Connection: close\r\n\r\n";

		socket.write_some(asio::buffer(sRequest.data(), sRequest.size()), ec);

		using namespace std::chrono_literals;
		std::this_thread::sleep_for(20s);

		context.stop();
		if (thrContext.joinable()) thrContext.join();
	}
#endif

    return 0;
}

void GrabSomeData(asio::ip::tcp::socket& socket)
{
	socket.async_read_some(asio::buffer(vBuffer.data(), vBuffer.size()),
		[&](std::error_code ec, std::size_t length)
		{
			if (!ec)
			{
				std::cout << "\n\nRead " << length << " bytes\n\n";

				for (int i = 0; i < length; ++i)
				{
					std::cout << vBuffer[i];
				}

				GrabSomeData(socket);
			}
			else
			{
				std::cout << ec.message() << std::endl;
			}
		}
	);
}